package com.wxpay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wxpay.common.PayException;
import com.wxpay.config.WxPayApiConfig;
import com.wxpay.config.WxPayConfig;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * 微信支付API工具类
 * @Author worlt
 * @Date 2025/4/22 上午12:02
 */
@Component
public class WxPay {

    private CloseableHttpClient httpClient;
    
    @Autowired
    private WxPayConfig wxPayConfig;
    
    @Autowired
    private WxPayApiConfig wxPayApiConfig;
    
    /**
     * 初始化微信支付客户端
     */
    @PostConstruct
    public void init() {
        try {
            // 加载商户私钥
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
                    new FileInputStream(wxPayConfig.getPrivateKeyPath()));

            // 获取证书管理器实例
            CertificatesManager certificatesManager = CertificatesManager.getInstance();
            // 向证书管理器增加需要自动更新平台证书的商户信息
            certificatesManager.putMerchant(wxPayConfig.getMchId(),
                    new WechatPay2Credentials(wxPayConfig.getMchId(),
                            new PrivateKeySigner(wxPayConfig.getSerialNo(), merchantPrivateKey)),
                    wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));

            // 从证书管理器获取验证器
            Verifier verifier = certificatesManager.getVerifier(wxPayConfig.getMchId());
            
            // 构建HttpClient
            httpClient = WechatPayHttpClientBuilder.create()
                    .withMerchant(wxPayConfig.getMchId(), wxPayConfig.getSerialNo(), merchantPrivateKey)
                    .withWechatPay(Collections.singletonList(verifier.getValidCertificate()))
                    .build();
        } catch (Exception e) {
            throw new RuntimeException("初始化微信支付客户端失败", e);
        }
    }

    /**
     * 发起JSAPI支付
     *
     * @param outTradeNo  商户订单号
     * @param totalAmount 订单金额（单位：元）
     * @param description 商品描述
     * @param openid      用户openid
     * @return 返回JSAPI支付所需的参数
     * @throws PayException 支付异常
     */
    public JSONObject jsapiPay(String outTradeNo, String totalAmount, String description, String openid) throws PayException {
        try {
            // 创建请求
            HttpPost httpPost = new HttpPost(wxPayApiConfig.getJsapiPayV3Url());
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");

            // 构建请求参数
            Map<String, Object> requestMap = new HashMap<>();
            requestMap.put("appid", wxPayConfig.getAppId());
            requestMap.put("mchid", wxPayConfig.getMchId());
            requestMap.put("description", description);
            requestMap.put("out_trade_no", outTradeNo);
            requestMap.put("notify_url", wxPayApiConfig.getNotifyUrl());

            // 订单金额相关
            Map<String, Object> amountMap = new HashMap<>();
            // 微信支付金额单位为分，需要将元转换为分
            int amountInCents = (int) (Double.parseDouble(totalAmount) * 100);
            amountMap.put("total", amountInCents);
            amountMap.put("currency", "CNY");
            requestMap.put("amount", amountMap);

            // JSAPI支付必须传payer.openid
            Map<String, String> payerMap = new HashMap<>();
            payerMap.put("openid", openid);
            requestMap.put("payer", payerMap);

            // 将请求参数序列化为JSON
            String requestBody = JSON.toJSONString(requestMap);
            StringEntity entity = new StringEntity(requestBody, "UTF-8");
            entity.setContentType("application/json");
            httpPost.setEntity(entity);

            // 发送请求
            CloseableHttpResponse response = httpClient.execute(httpPost);
            String responseBody = EntityUtils.toString(response.getEntity());
            int statusCode = response.getStatusLine().getStatusCode();

            // 检查响应状态码
            if (statusCode >= 200 && statusCode < 300) {
                JSONObject jsonResponse = JSONObject.parseObject(responseBody);
                // 预支付交易会话标识
                String prepayId = jsonResponse.getString("prepay_id");
                if (prepayId == null) {
                    throw new PayException("获取prepay_id失败：" + responseBody);
                }
                
                // 构造小程序调用支付的参数
                JSONObject payParams = generateMiniPayParams(prepayId);
                return payParams;
            } else {
                throw new PayException("微信支付下单失败，状态码：" + statusCode + "，响应内容：" + responseBody);
            }
        } catch (IOException e) {
            throw new PayException("微信支付请求异常: " + e.getMessage());
        } catch (Exception e) {
            throw new PayException("微信支付异常: " + e.getMessage());
        }
    }
    
    /**
     * 生成小程序调起支付API需要的参数
     * @param prepayId 预支付交易会话标识
     * @return 支付参数
     */
    private JSONObject generateMiniPayParams(String prepayId) throws Exception {
        // 小程序调起支付的时间戳，单位：秒
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        // 随机字符串，长度不超过32位
        String nonceStr = java.util.UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
        
        // 签名类型
        String signType = "RSA";
        
        // 构造签名字符串
        String message = wxPayConfig.getAppId() + "\n" +
                         timestamp + "\n" +
                         nonceStr + "\n" +
                         "prepay_id=" + prepayId + "\n";
                         
        // 加载商户私钥
        PrivateKey privateKey = PemUtil.loadPrivateKey(new FileInputStream(wxPayConfig.getPrivateKeyPath()));
        
        // 使用SHA256withRSA算法进行签名
        java.security.Signature sign = java.security.Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(message.getBytes(StandardCharsets.UTF_8));
        byte[] signBytes = sign.sign();
        String paySign = java.util.Base64.getEncoder().encodeToString(signBytes);
        
        // 构造返回结果
        JSONObject result = new JSONObject();
        result.put("timeStamp", timestamp);
        result.put("nonceStr", nonceStr);
        result.put("package", "prepay_id=" + prepayId);
        result.put("signType", signType);
        result.put("paySign", paySign);
        
        return result;
    }
}
